/*
 *      net-sim - networkProtocols.IP.RoutingTable
 * 
 * 		RoutingTable.java
 *      
 *      Copyright 23/12/2008 nicolas
 *      
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 3 of the License, or
 *      (at your option) any later version.
 *      
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *      
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *      MA 02110-1301, USA.
 */
package networkProtocols.IP.RoutingTable;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import net_interfaces.Interface;
import networkProtocols.IP.Address.IpAddress;
import networkProtocols.IP.Address.Mask;
import exceptions.AddressException;
import exceptions.NodeException;
  
// TODO: Auto-generated Javadoc
/**
 * The Class RoutingTable.
 */
public class RoutingTable {
  
  /** The route t. */
  Map<Integer, RoutingEntry> routeT = null;  // Tabla de ruteo, compuesta por entradas
  
  /** The t. */
  Integer t=1;

  /**
   * Instantiates a new routing table.
   */
  public RoutingTable(){
    routeT=new HashMap<Integer, RoutingEntry>();
  }

  // Agrega una entrada, si ya existe (es decir existe dest y mask), la elimina y la 
  // reemplaza por la nueva
  /**
   * Adds the entry.
   * 
   * @param dst the dst
   * @param m the m
   * @param type the type
   * @param nh the nh
   * @param ifc the ifc
   */
  public void addEntry(IpAddress dst, Mask m, boolean type, IpAddress nh, Interface ifc) {
    Integer rem = null;
    Set<Integer> keySet = routeT.keySet();
    for (Integer elementKey : keySet) {
      RoutingEntry i = routeT.get(elementKey);
      if(i.equals(dst,m)) {
        rem = elementKey;
        break;
      }
    }
    if (rem != null) routeT.remove(rem);
    routeT.put(t, new RoutingEntry(dst, m, type, nh, ifc));
    t++;
  }
  // Agrega una entrada por defecto, si ya existe la entrada por defecto, se sobreescribe la interfaz
  // y el next hop
  /**
   * Adds the default.
   * 
   * @param nh the nh
   * @param ifc the ifc
   * 
   * @throws NodeException the node exception
   */
  public void addDefault(IpAddress nh, Interface ifc) throws NodeException {
    Integer rem = null;
    Set<Integer> keySet = routeT.keySet();
    for (Integer elementKey : keySet) {
      RoutingEntry i = routeT.get(elementKey);
      if(i.isdefault()) {
        rem = elementKey;
        break;
      }
    }
    if (rem != null) routeT.remove(rem);
    try {
      routeT.put(t, new RoutingEntry(new IpAddress(new Integer(0)), new Mask(new Integer(0)), false, nh, ifc));
      t++;
    } catch(AddressException e) {  }
  }

  // Elimina una entrada, dada su dest y mask. Si la entrada no esta, ignora
  /**
   * Del route.
   * 
   * @param dst the dst
   * @param m the m
   */
  public void delRoute(IpAddress dst, Mask m) {
    Integer rem = null;
    Set<Integer> keySet = routeT.keySet();
    for (Integer elementKey : keySet) {
      RoutingEntry i = routeT.get(elementKey);
      if(i.getDestination().equals(dst) && i.getMask().equals(m)) {
        rem = elementKey;
        break;
      }
    }
    if (rem != null) routeT.remove(rem);
  }

  // Dada una direccion de destino, devuelve la entrada que contiene al next hop, segun
  // metodo longest match prefix
  // Si no hay matching y hay default, devuelve la entrada default.
  // Si no consigue matching ni default, devuelve null, lo que indica red desconocida
  /**
   * Gets the next hop.
   * 
   * @param dest the dest
   * 
   * @return the next hop
   */
  public RoutingEntry getNextHop(IpAddress dest) {
    int match = -1;                  // Bits de matching de la entrada con mayor matching
    RoutingEntry matchingentry = null;
    Set<Integer> keySet = routeT.keySet();
    for (Integer elementKey : keySet) {
      RoutingEntry i = routeT.get(elementKey);
      int m = i.getmatching(dest);       // Obtiene matching de la entrada
      if(m>match) {                      // mejora matching existente?
        match = m;
        matchingentry = i;               //routeT.get(elementKey);
      }
    }
    return matchingentry;
  }

  /* (non-Javadoc)
   * @see java.lang.Object#toString()
   */
  public String toString() {
    Set<Integer> keySet = routeT.keySet();
    for (Integer elementKey : keySet) {
      RoutingEntry i = routeT.get(elementKey);
      System.out.println("RoutingEntry: "+ i.toString());
    }
    return "eee";
  }
}
